﻿using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

 
using System; 
using System.Collections.Generic;
using System.IO;  
using System.Threading;
using System.Threading.Tasks;

namespace JGSY.SerialCommunication.UpperComputer.Component
{
    /// <summary>
    /// Modbus TCP 客户端实现
    /// </summary>
    public class ModbusTcpClient : IDisposable
    {
        private readonly TcpClient _tcpClient;
        private readonly string _ipAddress;
        private readonly int _port;
        private readonly int _timeout;
        private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(1, 1);
        private readonly CancellationTokenSource _disposeCts = new CancellationTokenSource();
        private int _transactionId = 0;
        private bool _disposed = false;

        /// <summary>
        /// 初始化 Modbus TCP 客户端
        /// </summary>
        /// <param name="ipAddress">服务器 IP 地址</param>
        /// <param name="port">服务器端口，默认 502</param>
        /// <param name="timeout">超时时间（毫秒），默认 1000</param>
        public ModbusTcpClient(string ipAddress, int port = 502, int timeout = 1000)
        {
            _ipAddress = ipAddress;
            _port = port;
            _timeout = timeout;
            _tcpClient = new TcpClient();
        }

        /// <summary>
        /// 连接到 Modbus TCP 服务器
        /// </summary>
        /// <returns></returns>
        public async Task ConnectAsync()
        {
            await _connectionLock.WaitAsync(_disposeCts.Token);
            try
            {
                if (!_tcpClient.Connected)
                {
                    await _tcpClient.ConnectAsync(IPAddress.Parse(_ipAddress), _port);
                    _tcpClient.ReceiveTimeout = _timeout;
                    _tcpClient.SendTimeout = _timeout;
                }
            }
            finally
            {
                _connectionLock.Release();
            }
        }

        /// <summary>
        /// 断开与服务器的连接
        /// </summary>
        public void Disconnect()
        {
            _connectionLock.Wait();
            try
            {
                if (_tcpClient.Connected)
                {
                    _tcpClient.Close();
                }
            }
            finally
            {
                _connectionLock.Release();
            }
        }

        /// <summary>
        /// 读取线圈 (功能码 0x01)
        /// </summary>
        /// <param name="slaveId">从站 ID</param>
        /// <param name="startAddress">起始地址</param>
        /// <param name="quantity">数量 (1-2000)</param>
        /// <returns>线圈状态数组</returns>
        public async Task<bool[]> ReadCoils(byte slaveId, ushort startAddress, ushort quantity)
        {
            ValidateQuantity(quantity, 2000);
            var response = await SendRequestAsync(slaveId, 0x01, startAddress, quantity);
            return ParseCoilResponse(response);
        }

        /// <summary>
        /// 读取离散输入 (功能码 0x02)
        /// </summary>
        /// <param name="slaveId">从站 ID</param>
        /// <param name="startAddress">起始地址</param>
        /// <param name="quantity">数量 (1-2000)</param>
        /// <returns>离散输入状态数组</returns>
        public async Task<bool[]> ReadDiscreteInputs(byte slaveId, ushort startAddress, ushort quantity)
        {
            ValidateQuantity(quantity, 2000);
            var response = await SendRequestAsync(slaveId, 0x02, startAddress, quantity);
            return ParseCoilResponse(response);
        }

        /// <summary>
        /// 读取保持寄存器 (功能码 0x03)
        /// </summary>
        /// <param name="slaveId">从站 ID</param>
        /// <param name="startAddress">起始地址</param>
        /// <param name="quantity">数量 (1-125)</param>
        /// <returns>寄存器值数组</returns>
        public async Task<ushort[]> ReadHoldingRegisters(byte slaveId, ushort startAddress, ushort quantity)
        {
            ValidateQuantity(quantity, 125);
            var response = await SendRequestAsync(slaveId, 0x03, startAddress, quantity);
            return ParseRegisterResponse(response);
        }

        /// <summary>
        /// 读取输入寄存器 (功能码 0x04)
        /// </summary>
        /// <param name="slaveId">从站 ID</param>
        /// <param name="startAddress">起始地址</param>
        /// <param name="quantity">数量 (1-125)</param>
        /// <returns>寄存器值数组</returns>
        public async Task<ushort[]> ReadInputRegisters(byte slaveId, ushort startAddress, ushort quantity)
        {
            ValidateQuantity(quantity, 125);
            var response = await SendRequestAsync(slaveId, 0x04, startAddress, quantity);
            return ParseRegisterResponse(response);
        }

        /// <summary>
        /// 写入单个线圈 (功能码 0x05)
        /// </summary>
        /// <param name="slaveId">从站 ID</param>
        /// <param name="address">线圈地址</param>
        /// <param name="value">线圈值</param>
        /// <returns></returns>
        public async Task WriteSingleCoil(byte slaveId, ushort address, bool value)
        {
            ushort coilValue = value ? (ushort)0xFF00 : (ushort)0x0000;
            await SendRequestAsync(slaveId, 0x05, address, coilValue);
        }

        /// <summary>
        /// 写入单个寄存器 (功能码 0x06)
        /// </summary>
        /// <param name="slaveId">从站 ID</param>
        /// <param name="address">寄存器地址</param>
        /// <param name="value">寄存器值</param>
        /// <returns></returns>
        public async Task WriteSingleRegister(byte slaveId, ushort address, ushort value)
        {
            await SendRequestAsync(slaveId, 0x06, address, value);
        }

        /// <summary>
        /// 写入多个线圈 (功能码 0x0F)
        /// </summary>
        /// <param name="slaveId">从站 ID</param>
        /// <param name="startAddress">起始地址</param>
        /// <param name="values">线圈值数组</param>
        /// <returns></returns>
        public async Task WriteMultipleCoils(byte slaveId, ushort startAddress, bool[] values)
        {
            if (values.Length == 0 || values.Length > 1968)
                throw new ArgumentException("线圈数量必须在 1-1968 之间");

            int byteCount = (values.Length + 7) / 8;
            byte[] data = new byte[byteCount];

            for (int i = 0; i < values.Length; i++)
            {
                if (values[i])
                {
                    int byteIndex = i / 8;
                    int bitIndex = i % 8;
                    data[byteIndex] |= (byte)(1 << bitIndex);
                }
            }

            await SendRequestAsync(slaveId, 0x0F, startAddress, (ushort)values.Length, data);
        }

        /// <summary>
        /// 写入多个寄存器 (功能码 0x10)
        /// </summary>
        /// <param name="slaveId">从站 ID</param>
        /// <param name="startAddress">起始地址</param>
        /// <param name="values">寄存器值数组</param>
        /// <returns></returns>
        public async Task WriteMultipleRegisters(byte slaveId, ushort startAddress, ushort[] values)
        {
            if (values.Length == 0 || values.Length > 123)
                throw new ArgumentException("寄存器数量必须在 1-123 之间");

            byte[] data = new byte[values.Length * 2];
            for (int i = 0; i < values.Length; i++)
            {
                BinaryPrimitives.WriteUInt16BigEndian(data.AsSpan(i * 2), values[i]);
            }

            await SendRequestAsync(slaveId, 0x10, startAddress, (ushort)values.Length, data);
        }

        /// <summary>
        /// 发送 Modbus TCP 请求并接收响应
        /// </summary>
        /// <param name="slaveId">从站 ID</param>
        /// <param name="functionCode">功能码</param>
        /// <param name="address">地址</param>
        /// <param name="value">值</param>
        /// <param name="data">额外数据</param>
        /// <returns>响应数据</returns>
        private async Task<byte[]> SendRequestAsync(byte slaveId, byte functionCode, ushort address, ushort value, byte[] data = null)
        {
            if (_disposed)
                throw new ObjectDisposedException(nameof(ModbusTcpClient));

            await _connectionLock.WaitAsync(_disposeCts.Token);
            try
            {
                if (!_tcpClient.Connected)
                    await ConnectAsync();

                ushort transactionId = GetNextTransactionId();
                byte[] request = BuildRequest(transactionId, slaveId, functionCode, address, value, data);

                using var cts = CancellationTokenSource.CreateLinkedTokenSource(
                    _disposeCts.Token,
                    new CancellationTokenSource(_timeout).Token
                );

                NetworkStream stream = _tcpClient.GetStream();
                await stream.WriteAsync(request, cts.Token);

                byte[] header = new byte[6];
                int headerBytesRead = await ReadExactAsync(stream, header, cts.Token);
                if (headerBytesRead != 6)
                    throw new IOException("读取响应头失败");

                ushort responseTransactionId = BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(0, 2));
                if (responseTransactionId != transactionId)
                    throw new InvalidDataException($"事务标识符不匹配，预期: {transactionId}，实际: {responseTransactionId}");

                ushort responseLength = BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(4, 2));
                byte[] responseData = new byte[responseLength];
                int dataBytesRead = await ReadExactAsync(stream, responseData, cts.Token);
                if (dataBytesRead != responseLength)
                    throw new IOException("读取响应数据失败");

                byte responseFunctionCode = responseData[0];
                if (responseFunctionCode == functionCode + 0x80)
                {
                    byte errorCode = responseData[1];
                    throw new ModbusException($"Modbus 异常: 功能码 {functionCode:X2}，错误码 {errorCode:X2}");
                }

                if (responseFunctionCode != functionCode)
                    throw new InvalidDataException($"功能码不匹配，预期: {functionCode:X2}，实际: {responseFunctionCode:X2}");

                return responseData;
            }
            finally
            {
                _connectionLock.Release();
            }
        }

        /// <summary>
        /// 构建 Modbus TCP 请求
        /// </summary>
        private byte[] BuildRequest(ushort transactionId, byte slaveId, byte functionCode, ushort address, ushort value, byte[] data)
        {
            int dataLength = data?.Length ?? 0;
            int pduLength = 2 + 2 + dataLength; // 地址(2) + 值(2) + 数据
            int totalLength = 6 + 1 + pduLength; // MBAP(6) + 从站ID(1) + PDU

            byte[] request = new byte[totalLength];

            // MBAP 头
            BinaryPrimitives.WriteUInt16BigEndian(request.AsSpan(0, 2), transactionId); // 事务标识符
            BinaryPrimitives.WriteUInt16BigEndian(request.AsSpan(2, 2), 0); // 协议标识符 (0)
            BinaryPrimitives.WriteUInt16BigEndian(request.AsSpan(4, 2), (ushort)(1 + pduLength)); // 长度
            request[6] = slaveId; // 从站 ID

            // PDU
            request[7] = functionCode;
            BinaryPrimitives.WriteUInt16BigEndian(request.AsSpan(8, 2), address);
            BinaryPrimitives.WriteUInt16BigEndian(request.AsSpan(10, 2), value);

            if (data != null && data.Length > 0)
            {
                request[12] = (byte)data.Length; // 字节计数
                Array.Copy(data, 0, request, 13, data.Length);
            }

            return request;
        }

        /// <summary>
        /// 解析线圈响应
        /// </summary>
        private bool[] ParseCoilResponse(byte[] response)
        {
            byte byteCount = response[1];
            bool[] values = new bool[byteCount * 8];

            for (int i = 0; i < byteCount; i++)
            {
                byte b = response[i + 2];
                for (int j = 0; j < 8; j++)
                {
                    int index = i * 8 + j;
                    if (index >= values.Length)
                        break;
                    values[index] = (b & (1 << j)) != 0;
                }
            }

            return values;
        }

        /// <summary>
        /// 解析寄存器响应
        /// </summary>
        private ushort[] ParseRegisterResponse(byte[] response)
        {
            byte byteCount = response[1];
            ushort[] values = new ushort[byteCount / 2];

            for (int i = 0; i < values.Length; i++)
            {
                values[i] = BinaryPrimitives.ReadUInt16BigEndian(response.AsSpan(2 + i * 2, 2));
            }

            return values;
        }

        /// <summary>
        /// 读取指定数量的字节
        /// </summary>
        private async Task<int> ReadExactAsync(NetworkStream stream, byte[] buffer, CancellationToken cancellationToken)
        {
            int totalBytesRead = 0;
            while (totalBytesRead < buffer.Length && !cancellationToken.IsCancellationRequested)
            {
                int bytesRead = await stream.ReadAsync(buffer.AsMemory(totalBytesRead), cancellationToken);
                if (bytesRead == 0)
                    break;
                totalBytesRead += bytesRead;
            }
            return totalBytesRead;
        }

        /// <summary>
        /// 获取下一个事务标识符
        /// </summary>
        private ushort GetNextTransactionId()
        {
            return (ushort)Interlocked.Increment(ref _transactionId);
        }

        /// <summary>
        /// 验证数量是否在允许范围内
        /// </summary>
        private void ValidateQuantity(ushort quantity, int max)
        {
            if (quantity < 1 || quantity > max)
                throw new ArgumentException($"数量必须在 1-{max} 之间");
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 释放资源的实现
        /// </summary>
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _disposeCts.Cancel();
                    _disposeCts.Dispose();
                    _connectionLock.Dispose();
                    Disconnect();
                    _tcpClient.Dispose();
                }
                _disposed = true;
            }
        }
    }

    ///// <summary>
    ///// Modbus 异常类
    ///// </summary>
    //public class ModbusException : Exception
    //{
    //    public ModbusException(string message) : base(message) { }
    //}
}